#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

#include "base/threads/PthreadInterface.h"

#define restrict 

/* Nasty work-around for stdlib calls to pthread functions
   before we have a pthreadInterface to handle them.  We
   support just non-reentrant use of a single lock.
*/
pthread_mutex_t* pending_pre_init_mutex = NULL;

int pthread_create (pthread_t* thread,
                    const pthread_attr_t* attr,
                    void *(*start_routine)(void*), 
                    void* arg) 
{
    return PthreadInterface::getPthreadInterface()->create(thread, attr, start_routine, arg);
}

pthread_t pthread_self() {
    return PthreadInterface::getPthreadInterface()->self();
}

int pthread_join (pthread_t thread, void** status) {
    return PthreadInterface::getPthreadInterface()->join(thread, status);

}

void pthread_exit (void* status) {
    PthreadInterface::getPthreadInterface()->exit(status);
    exit(0); // to avoid warnings
}

// lock functions

int pthread_mutex_init (pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) {
    mutex->PTHREAD_MUTEX_T_COUNT = 0;
    return 0;
}

int pthread_mutex_lock (pthread_mutex_t* lock) {
    if (PthreadInterface::getPthreadInterface() == NULL && pending_pre_init_mutex == NULL) {
        pending_pre_init_mutex = lock;
        return 0;
    }
    assert(pending_pre_init_mutex == NULL);
    return PthreadInterface::getPthreadInterface()->mutex_lock(lock);
}

int pthread_mutex_unlock (pthread_mutex_t* lock) {
    if (pending_pre_init_mutex == lock) {
        pending_pre_init_mutex = NULL;
        return 0;
    }
    assert(pending_pre_init_mutex == NULL);
    return PthreadInterface::getPthreadInterface()->mutex_unlock(lock);
}

int pthread_mutex_destroy (pthread_mutex_t* mutex) {
    return 0;
}

int pthread_rwlock_init (pthread_rwlock_t* lock, const pthread_rwlockattr_t* attr) {
    return PthreadInterface::getPthreadInterface()->rwlock_init(lock, attr);
}

int pthread_rwlock_destroy (pthread_rwlock_t* lock) {
    return PthreadInterface::getPthreadInterface()->rwlock_destroy(lock);
}

int pthread_rwlock_rdlock (pthread_rwlock_t* lock) {
    return PthreadInterface::getPthreadInterface()->rwlock_rdlock(lock);
}

int pthread_rwlock_wrlock (pthread_rwlock_t* lock) {
    return PthreadInterface::getPthreadInterface()->rwlock_wrlock(lock);
}

int pthread_rwlock_unlock (pthread_rwlock_t* lock) {
    return PthreadInterface::getPthreadInterface()->rwlock_unlock(lock);
}

int pthread_mutex_trylock (pthread_mutex_t* mutex) {
    return PthreadInterface::getPthreadInterface()->mutex_trylock(mutex);
}

// key functions

int pthread_key_create (pthread_key_t* key, void (*destructor)(void*)) {
    return PthreadInterface::getPthreadInterface()->key_create(key, destructor);
}

int pthread_setspecific (pthread_key_t key, const void* value) {
    return PthreadInterface::getPthreadInterface()->setspecific(key, value);
}

void* pthread_getspecific (pthread_key_t key) {
    return PthreadInterface::getPthreadInterface()->getspecific(key);
}

// conditional functions

int pthread_cond_init (pthread_cond_t* cond, const pthread_condattr_t* attr) {
    return PthreadInterface::getPthreadInterface()->cond_init(cond, attr);
}

int pthread_cond_destroy (pthread_cond_t* cond) {
    return PthreadInterface::getPthreadInterface()->cond_destroy(cond);
}

int pthread_cond_broadcast (pthread_cond_t* cond) {
    return PthreadInterface::getPthreadInterface()->cond_broadcast(cond);
}

int pthread_cond_wait (pthread_cond_t* cond, pthread_mutex_t* lock) {
    return PthreadInterface::getPthreadInterface()->cond_wait(cond, lock);
}

int pthread_cond_signal (pthread_cond_t* cond) {
    return PthreadInterface::getPthreadInterface()->cond_signal(cond);
}


// Functions that we want defined, but we don't use them
// All other functions are not defined so that they will cause a compile time
// error and we can decide if we need to do something with them

// functions really don't need to do anything

int pthread_yield() {
    // nothing else to yield to
    return 0;
}

// functions that deal with attributes (attributes are ignored)

int pthread_attr_init (pthread_attr_t* attr) {
    return 0;
}

int pthread_attr_setscope (pthread_attr_t* attr, int scope) {
    return 0;
}

int pthread_rwlockattr_init (pthread_rwlockattr_t* attr) {
    return 0;
}

int pthread_attr_setstacksize (pthread_attr_t* attr, size_t stacksize) {
    return 0;
}

int pthread_attr_setschedpolicy (pthread_attr_t* attr, int policy) {
    return 0;
}

// some functions that we don't really support

int pthread_setconcurrency (int new_level) {
    return 0;
}

// ... including any dealing with thread-leve signal handling
// (maybe we should throw an error message isntead?)

int pthread_sigmask (int how, const sigset_t* set, sigset_t* oset) {
    return 0;
}

int pthread_kill (pthread_t thread, int sig) __THROW {
    return PthreadInterface::getPthreadInterface()->kill(thread, sig);
}

int pthread_barrier_init (pthread_barrier_t *restrict barrier,
                          const pthread_barrierattr_t *restrict attr, unsigned count)
{
    return PthreadInterface::getPthreadInterface()->barrier_init(barrier, attr, count);
}

int pthread_barrier_destroy (pthread_barrier_t *barrier)
{
    return PthreadInterface::getPthreadInterface()->barrier_destroy(barrier);
}

int pthread_barrier_wait (pthread_barrier_t* barrier)
{
    return PthreadInterface::getPthreadInterface()->barrier_wait(barrier);
}

#ifndef __USE_EXTERN_INLINES
int pthread_equal (pthread_t t1, pthread_t t2)
{
    return PthreadInterface::getPthreadInterface()->equal(t1, t2);
}
#endif

// unimplemented pthread functions

int pthread_atfork (void (*)(void),
                    void (*)(void),
                    void (*)(void))
{
    assert(false);
}

int pthread_attr_destroy (pthread_attr_t*)
{
    assert(false);
}

int pthread_attr_getdetachstate (const pthread_attr_t*,
                                 int*)
{
    assert(false);
}

int pthread_attr_getguardsize (const pthread_attr_t* restrict a,
                               size_t *restrict b)
{
    assert(false);
}

int pthread_attr_getinheritsched (const pthread_attr_t *restrict,
                                  int *restrict)
{
    assert(false);
}

int pthread_attr_getschedparam (const pthread_attr_t *restrict,
                                struct sched_param *restrict)
{
    assert(false);
}

int pthread_attr_getschedpolicy (const pthread_attr_t *restrict,
                                 int *restrict)
{
    assert(false);
}

int pthread_attr_getscope (const pthread_attr_t *restrict,
                           int *restrict)
{
    assert(false);
}

int pthread_attr_getstack (const pthread_attr_t *restrict,
                           void* *restrict,
                           size_t *restrict)
{
    assert(false);
}

int pthread_attr_getstackaddr (const pthread_attr_t *restrict,
                               void* *restrict)
{
    assert(false);
}

int pthread_attr_getstacksize (const pthread_attr_t *restrict,
                               size_t *restrict)
{
    assert(false);
}

int pthread_attr_setdetachstate (pthread_attr_t*,
                                 int)
{
    assert(false);
}
int pthread_attr_setguardsize (pthread_attr_t*,
                               size_t)
{
    assert(false);
}

int pthread_attr_setinheritsched (pthread_attr_t*,
                                  int)
{
    assert(false);
}

int pthread_attr_setschedparam (pthread_attr_t *restrict,
                                const struct sched_param *restrict)
{
    assert(false);
}

int pthread_attr_setstack (pthread_attr_t*,
                           void*,
                           size_t)
{
    assert(false);
}

int pthread_attr_setstackaddr (pthread_attr_t*,
                               void*)
{
    assert(false);
}

int pthread_cancel (pthread_t)
{
    assert(false);
}

void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer,
                            void (*__routine) (void *),
                            void *__arg) __THROW
{
    assert(false);
}

void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer,
                           int __execute) __THROW
{
    assert(false);
}

int pthread_cond_timedwait (pthread_cond_t *restrict,
                            pthread_mutex_t *restrict,
                            const struct timespec *restrict)
{
    assert(false);
}

int pthread_condattr_destroy (pthread_condattr_t*)
{
    assert(false);
}

int pthread_condattr_getpshared (const pthread_condattr_t *restrict,
                                 int *restrict)
{
    assert(false);
}

int pthread_condattr_init (pthread_condattr_t*)
{
    assert(false);
}

int pthread_condattr_setpshared (pthread_condattr_t*,
                                 int)
{
    assert(false);
}

int pthread_detach (pthread_t)
{
    assert(false);
}


int pthread_getconcurrency (void)
{
    assert(false);
}

int pthread_getschedparam(pthread_t,
                          int *restrict,
                          struct sched_param *restrict)
{
    assert(false);
}

int pthread_key_delete (pthread_key_t)
{
    assert(false);
}

int pthread_mutex_getprioceiling (const pthread_mutex_t *restrict,
                                  int *restrict)
{
    assert(false);
}

int pthread_mutex_setprioceiling (pthread_mutex_t *restrict,
                                  int,
                                  int *restrict)
{
    assert(false);
}

int pthread_mutex_timedlock (pthread_mutex_t*,
                             const struct timespec*)
{
    assert(false);
}

int pthread_mutexattr_destroy (pthread_mutexattr_t*)
{
    assert(false);
}

int pthread_mutexattr_getprioceiling (const pthread_mutexattr_t *restrict,
                                      int *restrict)
{
    assert(false);
}

int pthread_mutexattr_getprotocol (const pthread_mutexattr_t *restrict,
                                   int *restrict)
{
    assert(false);
}

int pthread_mutexattr_getpshared (const pthread_mutexattr_t *restrict,
                                  int *restrict)
{
    assert(false);
}

int pthread_mutexattr_gettype (const pthread_mutexattr_t *restrict,
                               int *restrict)
{
    assert(false);
}

int pthread_mutexattr_init (pthread_mutexattr_t*)
{
    assert(false);
}

int pthread_mutexattr_setprioceiling (pthread_mutexattr_t*,
                                      int)
{
    assert(false);
}

int pthread_mutexattr_setprotocol (pthread_mutexattr_t*,
                                   int)
{
    assert(false);
}

int pthread_mutexattr_setpshared (pthread_mutexattr_t*,
                                  int)
{
    assert(false);
}

int pthread_mutexattr_settype (pthread_mutexattr_t*,
                               int)
{
    assert(false);
}

int pthread_rwlock_timedrdlock (pthread_rwlock_t *restrict,
                                const struct timespec *restrict)
{
    assert(false);
}

int pthread_rwlock_timedwrlock (pthread_rwlock_t *restrict,
                                const struct timespec *restrict)
{
    assert(false);
}

int pthread_rwlock_tryrdlock (pthread_rwlock_t*)
{
    assert(false);
}

int pthread_rwlock_trywrlock (pthread_rwlock_t*)
{
    assert(false);
}

int pthread_rwlockattr_destroy (pthread_rwlockattr_t*)
{
    assert(false);
}

int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *restrict,
                                   int *restrict)
{
    assert(false);
}

int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*,
                                  int)
{
    assert(false);
}

int pthread_setcancelstate (int,
                            int*)
{
    assert(false);
}

int pthread_setcanceltype (int,
                           int*)
{
    assert(false);
}

int pthread_setschedparam (pthread_t,
                           int,
                           const struct sched_param*)
{
    assert(false);
}

int pthread_setschedprio (pthread_t,
                          int)
{
    assert(false);
}

void pthread_testcancel (void)
{
    assert(false);
}

#if 0 // not implemented, but we can let default pthread impl. handle it

int pthread_once (pthread_once_t*,
                  void (*)(void))
{
    // it's safe to use pthread_once in system pthread library.
}

#endif
